/**************************************************************************
** Qt Creator license header template
**   Special keywords: Programmer 2012/4/11 2012
**   Environment variables: 
**   To protect a percent sign, use '%'.
**************************************************************************/
#include <QMessageBox>
#include "progressivemeshquadric.h"

void ProgressiveMesh(MyTriMesh& m, int targetFaceNum, bool selected, ProgressiveMeshQuadricParameters& pp,GLArea* gl)
{
    vcg::math::Quadric<double> QuadricZero;
    QuadricZero.SetZero();
    QuadricTemp TempData(m.vert,QuadricZero);
    Helper::TDp() = &TempData;

    if(selected) // simplify only inside selected faces
      {
        // select only the vertices having ALL incident faces selected
        vcg::tri::UpdateSelection<MyTriMesh>::VertexFromFaceStrict(m);

        // Mark not writable un-selected vertices
        MyTriMesh::VertexIterator  vi;
        for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
        {
              if(!(*vi).IsS()) (*vi).ClearW();
                          else (*vi).SetW();
        }
      }

      if(pp.PreserveBoundary && !selected)
        {
        pp.FastPreserveBoundary=true;
            pp.PreserveBoundary = false;
        }

      if(pp.NormalCheck) pp.NormalThrRad = M_PI/4.0;


      vcg::LocalOptimization<MyTriMesh> DeciSession(m,&pp);
        //cb(1,"Initializing simplification");
        DeciSession.Init<ProgressiveMeshQuadric>();

        //if(selected)
            //targetFaceNum= m.fn - (m.sfn-targetFaceNum);
        DeciSession.SetTargetSimplices(targetFaceNum);
        DeciSession.SetTimeBudget(0.1f); // this allow to update the progress bar 10 time for sec...
    //  if(TargetError< numeric_limits<double>::max() ) DeciSession.SetTargetMetric(TargetError);
      //int startFn=m.fn;
      int faceToDel = m.fn-targetFaceNum;
     while( DeciSession.DoOptimization() && m.fn > targetFaceNum )
     {
         //cb();
         gl->updateGL();
       //cb(100-100*(m.fn-TargetFaceNum)/(faceToDel), "Simplifying...");
     }

        DeciSession.Finalize<ProgressiveMeshQuadric>();

//      if(selected) // Clear Writable flags
//      {
//        MyTriMesh::VertexIterator  vi;
//        for(vi=m.vert.begin();vi!=m.vert.end();++vi)
//          if(!(*vi).IsD()) (*vi).SetW();
//      }


//      if(true)//par.getBool("AutoClean"))
//              {
//                  int nullFaces=vcg::tri::Clean<MyTriMesh>::RemoveFaceOutOfRangeArea(m,0);
//                  //if(nullFaces) Log( "PostSimplification Cleaning: Removed %d null faces", nullFaces);
//                  int deldupvert=vcg::tri::Clean<MyTriMesh>::RemoveDuplicateVertex(m);
//                  //if(deldupvert) Log( "PostSimplification Cleaning: Removed %d duplicated vertices", deldupvert);
//                  int delvert=vcg::tri::Clean<MyTriMesh>::RemoveUnreferencedVertex(m);
//                  //if(delvert) Log( "PostSimplification Cleaning: Removed %d unreferenced vertices",delvert);
//                  //m.clearDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER);
//                  //vcgtri::Allocator<CMeshO>::CompactVertexVector(m.cm);
//                  //tri::Allocator<CMeshO>::CompactFaceVector(m.cm);
//              }

              vcg::tri::UpdateNormals<MyTriMesh>::PerVertexNormalizedPerFace(m);
              vcg::tri::UpdateBounding<MyTriMesh>::Box(m);
}


//find the faces from vl to vr in CCW.
void FindEdgeSet(std::vector<vcg::face::VFIterator<MyFace> >& es,VSplit* s)
{
    MyVertex* nextv = s->vl;
    vcg::face::VFIterator<MyFace> vfi(s->v);

    //clear Visited flag
    for(; !vfi.End(); ++vfi)
        vfi.f->ClearV();

    while(true){
        vfi.f = s->v->VFp();
        vfi.z = s->v->VFi();
        for(; !vfi.End();++vfi){
            if(!vfi.f->IsV() && vfi.f->V((vfi.z+1)%3)==nextv){
                es.push_back(vfi);
                vfi.f->SetV();
                nextv = vfi.f->V((vfi.z+2)%3);
                break;
            }
        }
        if(s->vr==nextv){
            break;
        }

    }
}


void RestoreProgressiveMesh(MyTriMesh& m,ProgressiveMeshQuadricParameters& pp,GLArea* gl)
{
    std::vector<vcg::face::VFIterator<MyFace> > es;

    VSplit* s = NULL;
    MyTriMesh::VertexPointer vNew = NULL;//,vl,vr;

    MyFace* f1 = NULL;
    MyFace* f2 = NULL;
    int i = 10;
    while(!pp.vSplits.empty()){

        s = pp.getRemoveSplit();
        assert(!s->v->IsD() && !s->vl->IsD() && !s->vr->IsD());

        //add a new Vertex
        vNew = pp.getRemoveVertex();
        assert(vNew->IsD());
        vNew->ClearD();
        vNew->P() = s->v0;
        vNew->VFi() = -1;
        vNew->VFp() = NULL;
        m.vn++;



//        QMessageBox msg;
//        msg.setText(QString("size left:"+pp.vSplits.size()));
//        msg.setWindowTitle(QString("info"));
//        msg.exec();
        es.clear();
        FindEdgeSet(es,s);
        assert(!es.empty());

        //setting the VF topo for vNew
        for(std::vector<vcg::face::VFIterator<MyFace> >::iterator i=es.begin(); i!=es.end();i++){

            vcg::face::VFDetach(*((*i).f),(*i).z);
            (*i).f->V((*i).z) = vNew;
            vcg::face::VFAppend((*i).f,(*i).z);
        }

        //add two new faces.
        f1 = pp.getRemoveFace();
        f2 = pp.getRemoveFace();
        assert(f1->IsD() && f2->IsD());
        f1->ClearD();
        f2->ClearD();
        m.fn += 2;

        f1->V(0) = vNew;
        f1->V(1) = s->v;
        f1->V(2) = s->vl;
        vcg::face::VFAppend<MyFace>(f1,0);
        vcg::face::VFAppend<MyFace>(f1,1);
        vcg::face::VFAppend<MyFace>(f1,2);
        f2->V(0) = vNew;
        f2->V(1) = s->vr;
        f2->V(2) = s->v;
        vcg::face::VFAppend<MyFace>(f2,0);
        vcg::face::VFAppend<MyFace>(f2,1);
        vcg::face::VFAppend<MyFace>(f2,2);

        s->v->P() = s->v1;

        //vcg::tri::UpdateTopology<MyTriMesh>::FaceFace(m);
      // gl->updateGL();

        /////////////////////////////
    }
}

